<?
/**
 * Copyright 2007 Melange.
 *
 * This file is part of PHP-MVC.
 *
 * PHP-MVC is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * PHP-MVC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PHP-MVC; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category    Melange
 * @package     php-mvc
 * @subpackage  util
 * @copyright   Copyright (c) 2007 Jeroen Simons. All rights reserved
 * @author      Jeroen Simons <jeroen@melange.nl>
 * @link        http://www.melange.nl/
 *
 */


/**
 * <p>General purpose utility methods related to processing a servlet request
 * in the Struts controller framework.</p>
 */
class RequestUtils {


    // --------------------------------------------------------- Public Methods


    /**
     * <p>Create (if necessary) and return an <code>ActionForm</code> instance
     * appropriate for this request.  If no <code>ActionForm</code> instance
     * is required, return <code>null</code>.</p>
     *
     * @param request      The servlet request we are processing
     * @param mapping      The action mapping for this request
     * @param moduleConfig The configuration for this module
     * @param servlet      The action servlet
     * @return ActionForm instance associated with this request
     */
    public static function createActionForm(Request $request, ActionConfig $mapping,
        ModuleConfig $moduleConfig) {

        $log =& LoggerManager::getLogger("RequestUtils");

        /*
         * Is there a form bean associated with this mapping?
         * Todo: change $mapping->getType() back to $mapping->getAttribute()
         * Using the type (nl/melange/Form) instead of the attribute for
         * __autoloading the instance
         *
         */
        $attribute = $mapping->getAttribute();

        if (is_null($attribute)) {
            return null;
        }

        if($attribute == "unknown") {
            $attribute = addslashes($request->getPathInfo());
        }

        // Look up the form bean configuration information to use
        $name = $mapping->getName();
        $config = $moduleConfig->findFormBeanConfig($name);

        if (is_null($config)) {
            if($log->isDebugEnabled())
                $log->debug("No FormBeanConfig found under '" . $name . "'");
            return null;
        }

//        $prefix = $moduleConfig->getPrefix();

//        // Add a slash for no default modules
//        if(strlen($prefix) > 0)
//            $prefix .= "/";

        // The Substracted ClassName of this FormBean, ie: UserForm.
        include_once($config->getType().".php");

        $instance =
            self::lookupActionForm($request, $attribute, $mapping->getScope());

        // Can we recycle the existing form bean instance (if there is one)?
        if (!is_null($instance)) {
            return ($instance);
        }

        // The Substracted ClassName of this FormBean, ie: UserForm.
        $class = substr(strrchr($config->getType(), "/"), 1);

        // Initialize the FormBean.
        return new $class();
    }


    public static function populate(&$form, Request $request) {

        $log =& LoggerManager::getLogger("RequestUtils");

        $params =& $request->getParameterMap();
        while(list($key, $value) = each($params)) {

            if($log->isDebugEnabled()) {
                $log->debug("");
                $log->debug("-------------------------------");
                $log->debug($key." = ".$value);
            }

            // ImageButtonBean support.
            if(preg_match("/_(x|y)$/", $key))
                $key = preg_replace("/_(x|y)$/", "->$1", $key);

            $paths = explode("->", $key);

            $object = $form;

            for($i=0; $i<count($paths); $i++) {
                if($log->isDebugEnabled())
                    $log->debug("Now in path : ".$paths[$i]);

                if($i < count($paths) - 1) {

                    $method = "get".ucfirst($paths[$i]);
                    if($log->isDebugEnabled())
                        if($log->isDebugEnabled())
                            $log->debug("Looking for method ".$method." in ".get_class($object));
                    if(method_exists($object, $method)) {
                        if($log->isDebugEnabled())
                            $log->debug("Calling method : ".get_class($object)."->".$method."()");
                        $object = call_user_func(array($object, $method));
                    }

                } else {

                    $method = "set".ucfirst($paths[$i]);
                    if($log->isDebugEnabled())
                        $log->debug("Looking for method ".$method." in ".get_class($object));
                    if(method_exists($object, $method)) {
                        if($log->isDebugEnabled())
                            $log->debug("Calling method : ".get_class($object)."->".$method."(".$value.")");
                        call_user_func(array($object, $method), $value);
                    }

                }
            }
        }
    }

    public static function lookupActionForm(Request $request,
        $attribute, $scope) {

        $log =& LoggerManager::getLogger("RequestUtils");

        // Look up any existing form bean instance
        if ($log->isDebugEnabled()) {
            $log->debug(" Looking for ActionForm bean instance in scope '"
                . $scope . "' under attribute key '" . $attribute . "'");
        }

        $instance = null;
        $session = null;

        if ("request" == $scope) {
            $instance =& $request->getAttribute($attribute);
        } else {

            $session = $request->getSession(true);
            if($session !== null)
                $instance = $session->getAttribute($attribute);

        }

        return ($instance);
    }


    /**
     * <p>Return the context-relative URL that corresponds to the specified
     * <code>ForwardConfig</code>. The URL is calculated based on the
     * properties of the {@link ForwardConfig} instance as follows:</p>
     *
     * <ul>
     *
     * <li>If the <code>contextRelative</code> property is set, it is assumed
     * that the <code>path</code> property contains a path that is already
     * context-relative: <ul>
     *
     * <li>If the <code>path</code> property value starts with a slash, it is
     * returned unmodified.</li> <li>If the <code>path</code> property value
     * does not start with a slash, a slash is prepended.</li>
     *
     * </ul></li>
     *
     * <li>Acquire the <code>forwardPattern</code> property from the
     * <code>ControllerConfig</code> for the application module used to
     * process this request. If no pattern was configured, default to a
     * pattern of <code>$M$P</code>, which is compatible with the hard-coded
     * mapping behavior in Struts 1.0.</li>
     *
     * <li>Process the acquired <code>forwardPattern</code>, performing the
     * following substitutions: <ul> <li><strong>$M</strong> - Replaced by the
     * module prefix for the application module processing this request.</li>
     *
     * <li><strong>$P</strong> - Replaced by the <code>path</code> property of
     * the specified {@link ForwardConfig}, prepended with a slash if it does
     * not start with one.</li>
     *
     * <li><strong>$$</strong> - Replaced by a single dollar sign
     * character.</li>
     *
     * <li><strong>$x</strong> (where "x" is any charater not listed above) -
     * Silently omit these two characters from the result value.  (This has
     * the side effect of causing all other $+letter combinations to be
     * reserved.)</li>
     *
     * </ul></li></ul>
     *
     * @param request      The servlet request we are processing
     * @param forward      ForwardConfig to be evaluated
     * @param moduleConfig Base forward on this module config.
     * @return context-relative URL
     * @since Struts 1.2
     */
    public static function forwardURL(Request $request,
        ForwardConfig $forward, ModuleConfig $moduleConfig, $redirect=false) {

        //load the current moduleConfig, if null
//        if ($moduleConfig === null) {
//            moduleConfig = ModuleUtils.getInstance().getModuleConfig(request);
//        }

        $path = $forward->getPath();

        //load default prefix
        $prefix = $moduleConfig->getPrefix();

        //override prefix if supplied by forward
        if ($forward->getModule() !== null) {
            $prefix = $forward->getModule();

            if ("/" == $prefix) {
                $prefix = "";
            }
        }

        $sb = "";

        // Calculate a context relative path for this ForwardConfig
        $forwardPattern =
            $moduleConfig->getControllerConfig()->getForwardPattern();

        if ($forwardPattern === null || $redirect) {
            // Performance optimization for previous default behavior
            $sb .= $prefix;

            // smoothly insert a '/' if needed
            if (!strpos($path, "/") == 0) {
                $sb .= "/";
            }

            $sb .= $path;

        } else {

            $dollar = false;

            for ($i = 0; $i < strlen($forwardPattern); $i++) {
                $ch = $forwardPattern[$i];

                if ($dollar) {
                    switch ($ch) {

                        case 'M':

                            $sb .= $prefix;
                            break;

                        case 'P':

                            // add '/' if needed
                            if (!strpos($path, "/") == 0) {
                                $sb .= "/";
                            }
                            $sb .= $path;
                            break;

                        case '$':

                            $sb .= '$';
                            break;

                        default:
                            ; // Silently swallow
                    }

                    $dollar = false;
                    continue;

                } else if ($ch == '$') {
                    $dollar = true;
                } else {
                    $sb .= $ch;
                }
            }
        }

        return ($sb);
    }


    /**
     * <p>Look up and return current user locale, based on the specified
     * parameters.</p>
     *
     * @param request The request used to lookup the Locale
     * @param locale  Name of the session attribute for our user's Locale.  If
     *                this is <code>null</code>, the default locale key is
     *                used for the lookup.
     * @return current user locale
     * @since Struts 1.2
     */
    public static function getUserLocale(Request $request, $locale) {
        $userLocale = null;
        $session = $request->getSession();

        if ($locale === null) {
            $locale = MVC::LOCALE_KEY;
        }

        // Only check session if sessions are enabled
        if ($session !== null) {
            $userLocale = $session->getAttribute($locale);
        }

        if ($userLocale === null) {
            // Returns Locale based on Accept-Language header or the server default
            $userLocale = $request->getLocale();
        }

        return $userLocale;
    }

}